package ast

import (
	"fmt"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"

	"gitee.com/u-language/u-language/ucom/astdata"
	"gitee.com/u-language/u-language/ucom/data"
	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/errutil"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

// Sbt 符号表 symbolTable
type Sbt struct {
	//单线程符号表
	vmap map[string]SymbolInfo
	//多线程符号表 key string value SymbolInfo
	syncmap *sync.Map
	//上级符号表
	sbt                 *Sbt
	importPackage       *map[string]*Package
	conuntNoAccessRange int64 //Count not accessed by Range
	//变量总大小，为兼容之前的VAT存在
	Size int64
	//是否并发
	Thread     bool
	isUseCount bool
}

// SymbolInfo 符号信息
type SymbolInfo struct {
	//符号信息
	Info fmt.Stringer
	//类型枚举
	Kind enum.Symbol
}

func (s *SymbolInfo) Name() string {
	switch s.Kind {
	case enum.SymbolVar:
		return s.PtrVarInfoSbt().Name
	case enum.SymbolFunc:
		return s.PtrFuncInfo().Name
	case enum.SymbolConst:
		return s.Info.(*ConstNode).Name
	case enum.SymbolStruct:
		return s.Info.(*StructDecl).Name
	case enum.SymbolEnum:
		return s.Info.(*EnumDecl).Name
	case enum.SymbolNoParameVar:
		return s.Info.(*VarNode).Name
	}
	panic(fmt.Errorf("未知的枚举值 %d", s.Kind))
}

func (s *SymbolInfo) Type() string {
	switch s.Kind {
	case enum.SymbolVar:
		return s.PtrVarInfoSbt().Type.Typ()
	case enum.SymbolFunc:
		return enum.Func
	case enum.SymbolConst:
		return s.Info.(*ConstNode).TYPE.Typ()
	case enum.SymbolStruct:
		return s.Info.(*StructDecl).Name
	case enum.SymbolEnum:
		return s.Info.(*EnumDecl).Name
	case enum.SymbolNoParameVar:
		return s.Info.(*VarNode).TYPE.Typ()
	}
	panic(fmt.Errorf("未知的枚举值 %s", s.Kind))
}

func (s SymbolInfo) String() string {
	var buf strings.Builder
	buf.WriteString("SymbolInfo{ Kind:")
	buf.WriteString(s.Kind.String())
	buf.WriteString(" Info: ")
	if s.Info != nil {
		buf.WriteString(s.Info.String())
	} else {
		buf.WriteString("<nil>")
	}
	buf.WriteString(" }")
	return buf.String()
}

// PtrVarVarInfoSbt 将SymbolInfo的Info字段解释为VarInfoSbt，并返回
func (s *SymbolInfo) PtrVarInfoSbt() VarInfoSbt {
	return s.Info.(VarInfoSbt)
}

// PtrFuncInfo 将SymbolInfo的Info字段解释为FuncInfo，并返回指针
func (s *SymbolInfo) PtrFuncInfo() *FuncInfo {
	return s.Info.(*FuncInfo)
}

// PtrStructDecl 将SymbolInfo的Info字段解释为StructInfo指针，并返回
func (s *SymbolInfo) PtrStructDecl() *StructDecl {
	return s.Info.(*StructDecl)
}

// NewSymbolInfo_Func 创建函数符号信息
func NewSymbolInfo_Func(info *FuncInfo) SymbolInfo {
	ret := SymbolInfo{Kind: enum.SymbolFunc}
	ret.Info = info
	return ret
}

func NewSymbolInfo_Var(info VarInfoSbt) SymbolInfo {
	ret := SymbolInfo{Kind: enum.SymbolVar}
	ret.Info = info
	return ret
}

// NewSbt 创建符号表
//   - Thread控制是否可以并发
func NewSbt(Thread bool) *Sbt {
	ret := new(Sbt)
	if Thread {
		ret.syncmap = new(sync.Map)
	} else {
		ret.vmap = make(map[string]SymbolInfo)
	}
	ret.Thread = Thread
	return ret
}

// seniorSbt 设置上级符号表
//   - sbt是上级符号表
func (v *Sbt) seniorSbt(sbt *Sbt) {
	v.sbt = sbt
}

// add 添加一个符号信息
func (s *Sbt) add(name string, Kind enum.Symbol, symbolinfo fmt.Stringer) errcode.ErrCode {
	if s.isUseCount {
		atomic.AddInt64(&s.conuntNoAccessRange, 1)
	}
	info := SymbolInfo{Kind: Kind, Info: symbolinfo}
	if s.Thread { //多线程
		_, ok := s.syncmap.LoadOrStore(name, info) //记录到哈希表
		if ok {                                    //如果已经同名符号
			return errcode.SymbolRepeat
		}
	} else { //单线程
		_, ok := s.vmap[name]
		if ok { //如果已经同名符号
			return errcode.SymbolRepeat
		} else {
			s.vmap[name] = info //记录到哈希表
		}
	}
	return errcode.NoErr
}

// store 设置一个符号信息
func (s *Sbt) store(name string, Kind enum.Symbol, symbolinfo fmt.Stringer) {
	info := SymbolInfo{Kind: Kind, Info: symbolinfo}
	if s.Thread { //多线程
		s.syncmap.Store(name, info) //记录到哈希表
	} else { //单线程
		s.vmap[name] = info //记录到哈希表
	}
}

// AddVar 记录变量 有错返回错误码
//   - name是变量名
//   - Type是类型字符串
//   - IsStackAlloc是否在栈中分配
func (s *Sbt) AddVar(name string, Type astdata.Typ, IsStackAlloc bool, LineNum int, FileName string) errcode.ErrCode {
	err := s.add(name, enum.SymbolVar, NewVarInfoSbt(name, Type, IsStackAlloc, LineNum, FileName))
	if utils.Is_Enable_amd64 {
		atomic.AddInt64(&s.Size, RetTypeSize(Type.Typ()))
	}
	return err
}

// AddConst 记录常量 有错返回错误码
// - n 是被记录的常量节点
func (s *Sbt) AddConst(n *ConstNode) errcode.ErrCode {
	return s.add(n.Name, enum.SymbolConst, n)
}

// AddFunc 记录函数
//   - info是函数信息
func (v *Sbt) AddFunc(info *FuncInfo) errcode.ErrCode {
	if info.Name == "main" {
		if len(info.RetValue) != 0 {
			return errcode.MainNoRetValue
		}
		info.RetValue = append(info.RetValue, astdata.NewNameAndType("", NewObject(TypeObj, "int")))
	}
	return v.addFunc(info)
}

// addFunc 记录函数
//   - info是函数信息
func (s *Sbt) addFunc(info *FuncInfo) errcode.ErrCode {
	return s.add(info.Name, enum.SymbolFunc, info)
}

// AddStruct 记录结构体 有错返回错误码
//   - name是变量名
//   - FieldTable是字段表
func (s *Sbt) AddStruct(name string, decl *StructDecl) errcode.ErrCode {
	return s.add(name, enum.SymbolStruct, decl)
}

// AddMethod 记录方法 有错返回错误码
//   - Type是方法所属的类型名
//   - FuncInfo是函数的信息
func (s *Sbt) AddMethod(Type string, method *MethodNode) errcode.ErrCode {
	name := astdata.Generate_method_symbol(Type, method.FuncInfo.Name)
	table_name := Type + enum.Method
	info, ok := s.have(table_name)
	if !ok {
		table := NewMethodTableInfo(s.Thread)
		s := s
		for s.sbt != nil {
			s = s.sbt
		}
		if s.add(table_name, enum.SymbolMethodTable, table) != errcode.NoErr { //并发时万一其他goroutine先添加
			info, _ = s.have(table_name)
		} else {
			info = SymbolInfo{Info: table}
		}
	}
	s.add(name, enum.SymbolMethod, method)
	v := info.Info.(MethodTableInfo)
	return v.Infos.add(name, enum.SymbolMethod, method)
}

// AddEnum 记录枚举 有错返回错误码
// - n 是被记录的枚举节点
func (s *Sbt) AddEnum(n *EnumDecl) errcode.ErrCode {
	return s.add(n.Name, enum.SymbolEnum, n)
}

// AddGenInst 记录泛型实例化信息
//   - node是泛型实例化节点
func (s *Sbt) AddGenInst(node *GenericInstantiation) {
	typ := node.Typ()
	if typ == "" {
		return
	}
	name := utils.Ret_no_lea(typ)
	s.add(name, enum.SymbolGenInst, node)
}

// HaveMethod 查询是否记录了方法
//   - Type是类型名
//   - Name是函数的名字
//
// 如果符号不存在，返回空信息
func (s *Sbt) HaveMethod(Type string, Name string) SymbolInfo {
	defer func() {
		if err := recover(); err != nil {
			if _, ok := err.(errutil.Err2); ok {
				return
			}
			panic(err)
		}
	}()
	info := s.Have(Type + enum.Method)
	info = info.Info.(MethodTableInfo).Infos.Have(astdata.Generate_method_symbol(Type, Name))
	return info
}

func (v *Sbt) String() string {
	var buf strings.Builder
	buf.WriteString("&ast.Sbt{")
	v.Range(func(key string, value SymbolInfo) bool {
		buf.WriteString("\n")
		buf.WriteString(key)
		buf.WriteString(" : ")
		buf.WriteString(value.String())
		return true
	})
	buf.WriteString(" size: ")
	buf.WriteString(strconv.FormatInt(v.Size, 10))
	buf.WriteString("\t}")
	return buf.String()
}

// Range 循环遍历所有表中记录的符号信息
func (v *Sbt) Range(f func(key string, value SymbolInfo) bool) {
	if !v.isUseCount {
		if v.Thread {
			v.syncmap.Range(func(key, value interface{}) bool {
				return f(key.(string), value.(SymbolInfo))
			})
		} else {
			for key, value := range v.vmap {
				if !f(key, value) {
					return
				}
			}
		}
		return
	}
	for atomic.LoadInt64(&v.conuntNoAccessRange) > 0 {
		if v.Thread {
			v.syncmap.Range(func(key, value interface{}) bool {
				v.delete(key.(string))
				return f(key.(string), value.(SymbolInfo))
			})
		} else {
			for key, value := range v.vmap {
				v.delete(key)
				if !f(key, value) {
					return
				}
			}
		}
	}
}

func (v *Sbt) Copy() *Sbt {
	ret := NewSbt(v.Thread)
	v.Range(func(key string, value SymbolInfo) bool {
		ret.add(key, value.Kind, value.Info)
		return true
	})
	ret.sbt = v.sbt
	return ret
}

// Have 查询是否记录了符号
//   - name是符号名
//
// 如果符号不存在，被设计为程序不应继续执行，将panic
func (v *Sbt) Have(name string) SymbolInfo {
	value, _ := v.Have2(name)
	return value
}

// Have2 查询是否记录了符号
//   - name是符号名
//
// 如果符号不存在，被设计为程序不应继续执行，将panic
func (v *Sbt) Have2(name string) (SymbolInfo, bool) {
	if utils.Is_In_AutoFree(name) {
		name = name[:len(name)-len(enum.AutoFreeInFunc)]
	}
	info, ok := v.have(name)
	if ok {
		return info, true
	}
	info, ok = Builtin_func_info[name]
	if ok {
		return info, true
	}
	if v.sbt != nil { //如果存在上级符号表
		return v.sbt.Have(name), false
	}
	panic(errutil.NewErr2(errutil.UnknownSymbol, name))
}

// have 查询是否记录某个符号
func (s *Sbt) have(name string) (SymbolInfo, bool) {
	if s.Thread { //多线程
		ret, ok := s.syncmap.Load(name)
		if ok {
			return ret.(SymbolInfo), true
		}
	} else { //单线程
		ret, ok := s.vmap[name]
		if ok {
			return ret, true
		}
	}
	return SymbolInfo{}, false
}

func (s *Sbt) delete(name string) {
	if s.isUseCount {
		atomic.AddInt64(&s.conuntNoAccessRange, -1)
	}
	if s.Thread {
		s.syncmap.Delete(name)
		return
	}
	delete(s.vmap, name)
}

// HaveType 查询是否记录了指定类型的符号
//   - name是符号名
//
// 如果符号不存在，被设计为程序不应继续执行，将panic
func (s *Sbt) HaveType(name string) SymbolInfo {
	name = utils.Ret_no_lea(name)
	ret, ok := s.have(name)
	//如果符号不存在，或符号存在但不是结构体符号或枚举符号,并且同时不是内置类型
	if (!ok || utils.IsNoType(ret.Kind)) && !check_is_type(name) {
		if s.sbt != nil { //如果存在上级符号表
			return s.sbt.HaveType(name)
		}
		panic(errutil.NewErr2(errutil.UnknownType, name))
	}
	return ret
}

func (s *Sbt) haveImport(name string) (ok bool) {
	defer func() {
		if err := recover(); err != nil {
			ok = false
		}
	}()
	info, _ := s.have(name)
	return info.Kind == enum.SymbolImport
}

// VarInfoSbt 变量符号信息
type VarInfoSbt struct {
	Name string
	Type astdata.Typ
	//后两个用于出错时报错
	FileName string
	LineNum  int
	//是否是全局
	IsStackAlloc bool
}

// 创建变量符号信息
func NewVarInfoSbt(Name string, Type astdata.Typ, IsStackAlloc bool, LineNum int, FileName string) VarInfoSbt {
	return VarInfoSbt{Name: Name, Type: Type, IsStackAlloc: IsStackAlloc, LineNum: LineNum, FileName: FileName}
}

func (info VarInfoSbt) String() string {
	return fmt.Sprintf("ast.VarInfoSbt{Name:%s Type:%s IsStackAlloc:%v LineNum:%d FileName:%s}", info.Name, info.Type, info.IsStackAlloc, info.LineNum, info.FileName)
}

// Deprecated:暂不维护
// RetTypeSize 返回类型字节大小
func RetTypeSize(Type string) int64 {
	switch Type {
	case enum.Int:
		return 8
	case enum.Float:
		return 4
	case enum.Bool:
		return 1
	}
	return 0
}

// findTypeDep 寻找类型的依赖
func findTypeDep(sbt *Sbt, typ astdata.Typ) data.IsItARecursiveType {
	var info SymbolInfo
	if o, ok := typ.(*Object); ok {
		info = sbt.HaveType(o.Name)
	} else {
		o := typ.(*Objects)
		info = sbt.HaveType(utils.GeneratePackageSymbol(o.Slice[0].Name, o.Slice[1].Name))
	}
	if info.Kind == enum.SymbolStruct {
		return info.Info.(*StructDecl)
	}
	return nil
}

// fieldInfo 字段信息
type fieldInfo struct {
	typ astdata.Typ
	dep data.IsItARecursiveType
}

func (info fieldInfo) My() string {
	return info.typ.Typ()
}

func (info fieldInfo) DepLen() int {
	return 1
}

func (info fieldInfo) Dep(i int) data.IsItARecursiveType {
	if i != 0 {
		panic(errutil.NewErr2(errutil.CompileErr, "访问越界"))
	}
	return info.dep
}

func (info fieldInfo) String() string {
	return fmt.Sprintf("typ:%s\tdep:%+v", info.typ, info.dep)
}

// ImportInfo 导入信息
type ImportInfo struct {
	Path string
}

func (info ImportInfo) String() string {
	return fmt.Sprintf("ast.ImportInfo{Path:%s}", info.Path)
}

type RemoveGenericsInfo struct {
	FileName string
	Nodes    []Node
	LineNum  int
}

func (info RemoveGenericsInfo) String() string {
	var buf strings.Builder
	for _, v := range info.Nodes {
		buf.WriteString(v.String())
	}
	return buf.String()
}

type MethodTableInfo struct {
	Infos *Sbt
}

func NewMethodTableInfo(Thread bool) MethodTableInfo {
	return MethodTableInfo{NewSbt(Thread)}
}

func (info MethodTableInfo) String() string {
	return info.Infos.String()
}
